package net.prasenjit.test.core.auth;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.text.MessageFormat;
import java.util.Collection;
import java.util.logging.Logger;

public class MethodNameAccessVoter implements AccessDecisionVoter<ReflectiveMethodInvocation> {

    private Logger LOGGER = Logger.getLogger(MethodNameAccessVoter.class.getName());

    @Override
    public boolean supports(ConfigAttribute attribute) {
        if (attribute.getAttribute().startsWith("*")) {
            return true;
        }
        return false;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        if (clazz.isAssignableFrom(MethodInvocation.class)) {
            return true;
        }
        return false;
    }

    @Override
    public int vote(Authentication authentication, ReflectiveMethodInvocation methodInvocation,
                    Collection<ConfigAttribute> attributes) {
        int result = ACCESS_ABSTAIN;
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

        for (ConfigAttribute attribute : attributes) {
            if (this.supports(attribute)) {
                result = ACCESS_DENIED;
                LOGGER.finest(MessageFormat.format("Access decider called for method {0}", methodInvocation.getMethod()));
                if (authentication.isAuthenticated()) {
                    String permission = derrivePermission(methodInvocation);
                    for (GrantedAuthority grantedAuthority : authorities) {
                        if (grantedAuthority.getAuthority().equals(permission)) {
                            return ACCESS_GRANTED;
                        }
                    }
                }
                LOGGER.finest("No rule found for other than method invocation");
            }
        }

        return result;
    }

    private String derrivePermission(ReflectiveMethodInvocation methodInvocation) {
        String methodName = methodInvocation.getMethod().getName();
        String className = methodInvocation.getMethod().getDeclaringClass().getSimpleName();
        className = className.replaceAll("Facade", "");
        return "PERMISSION_" + className.toUpperCase() + "_" + methodName.toUpperCase();
    }

}
